package gobvrtp

import (
	"context"
	"fmt"
	"io"

	"gogs.smarteye.com/servere/gobvrtp/dialog"
	"gogs.smarteye.com/servere/gobvrtp/rtpsdp"
	"gogs.smarteye.com/servere/gobvrtp/track"
	"gogs.smarteye.com/servere/gobvrtp/transport"
)

// Client rtp client, 只处理rtp协议, 传输音视频、文件由上层处理
type Client struct {
	ctx     context.Context
	ctxFunc context.CancelCauseFunc

	session *Session

	client transport.Transport
}

func NewClient() *Client {
	return &Client{
		session: NewSession(),
	}
}

func (c *Client) Session() *Session {
	return c.session
}

func (c *Client) Open(puId string, channel int, dialogInc dialog.PullDialogInc) error {

	// 根据track构建offer sdp
	// 发送sdp
	// 接收回复sdp
	// 连接服务器
	// 接收、发送rtp数据
	// 检查接收的rtp时间, 长时间无数据关闭
	offerSDP, err := c.session.CreateOffer()
	if err != nil {
		return err
	}

	dialogId, answerSDP, err := dialogInc.Open(context.TODO(), puId, channel, string(offerSDP))
	if err != nil {
		return err
	} else {
		_ = dialogId
		_ = answerSDP
	}

	if err = c.session.SetRemoteAnswer(answerSDP); err != nil {
		return err
	}

	objectAnswerSDP, err := rtpsdp.ParseSDPObject(answerSDP)
	if err != nil {
		return err
	}

	candidates := objectAnswerSDP.GetCandidates()
	for _, candidate := range candidates {
		switch candidate.Type {
		case track.NetworkTypeUDP4:
			ts, _ := transport.NewTransport("udp4", candidate.Host, candidate.Port, c.session.OnReadPacket)
			if err = ts.Open(); err != nil {
				return err
			}

			c.client = ts
		case track.NetworkTypeTCP4:
			ts, _ := transport.NewTransport("tcp4", candidate.Host, candidate.Port, c.session.OnReadPacket)
			if err = ts.Open(); err != nil {
				return err
			}

			c.client = ts
		}
	}

	if c.client == nil {
		return fmt.Errorf("error candidates")
	}

	// 发送rtcp实现打洞

	w, _ := c.getWriter()
	c.session.SetWriter(w)

	c.ctx, c.ctxFunc = context.WithCancelCause(context.TODO())

	for _, t := range c.session.tracks {
		t.Open()
	}

	return nil
}

func (c *Client) Wait() error {
	if c.ctx == nil {
		return fmt.Errorf("not open")
	}

	<-c.ctx.Done()

	return context.Cause(c.ctx)
}

func (c *Client) Close() error {
	if c.client != nil {
		c.client.Close()
	}

	if c.ctxFunc != nil {
		c.ctxFunc(fmt.Errorf("close"))
	}
	return nil
}

func (c *Client) getWriter() (io.Writer, bool) {
	return c.client, c.client != nil
}
